#include <stdio.h>
#include <stdlib.h>

/*osip2/eXosip2 header*/
#include "eXosip2/eXosip2.h"  
#include "eXosip2/eX_call.h"
#include "eXosip2/eX_setup.h"
#include "osipparser2/osip_parser.h" 
#include "osipparser2/osip_message.h"
#include "osipparser2/osip_body.h"

#include "od_def.h"
#include "od_trace.h"
#include "od_task.h"
#include "od_tube.h"
#include "od_os.h"
#include "od_msg.h"
#include "od_os_config.h"
#include "od_engine.h"
#include "od_sip_sdp.h"
#include "od_sip_comn.h"
#include "od_sip_state_machine.h"
#include "od_sip_invite.h"
#include "od_sip_message.h"
#include "od_sip_register.h"
#include "od_sip_timer.h"
#include "od_sip_event_distributor.h"


#define SIP_INVITE_TSK_STACK_SIZE    (4*1024 * 1024)   /*线程栈资源大小*/
#define NO_RESPONSE_TIME_OUT         (600)                //无响应等待时间约为45秒

typedef struct
{
    TASK_COMMON_MEMBERS

    int cid;
    pipeline_handle g_event_pipe;
    void **h_state_machine;         /**< handle of state machine pthread*/
    int did;                        /**< dialog id*/
    od_sip_ctx *ctx;
    od_sip_session_ctx session_ctx;
    eXosip_event_t *event;
    int wait_pick_up_cnt;           /*when INVITE send, start to count*/   
    int group_id;
    od_sip_event_queue evt_que;
}od_sip_invite_obj;


params_def od_sip_invite_dynamic_params_def[]=
{
    {OFFSET(od_sip_invite_dynamic_params, dst_uri), PT_VAR_STR | PT_MALLOC_FREE, sizeof(int), 0, 64},
    {OFFSET(od_sip_invite_dynamic_params, invite_status), PT_VAR_INT32 | PT_REAL_TIME | ODPT_READONLY, sizeof(int), 0, 1},
    {OFFSET(od_sip_invite_dynamic_params, bye_flg), PT_VAR_INT32 | PT_REAL_TIME, sizeof(int), 0, 1},
    {OFFSET(od_sip_invite_dynamic_params, send_invite_flg), PT_VAR_INT32 | PT_MALLOC_FREE, sizeof(int), 0, 1},     
    {0, 0, 0, 0, 0}
};

const od_sip_invite_dynamic_params glb_sip_invite_dynamic_params_default = 
{
    "1100",   /*dst_name*/
    0,        /*invite_flg*/
    0,        /*bye_flg*/
    0         /*send_invite_flg*/
};

static void sip_invite_reset_all_status_flg(od_sip_invite_obj *obj)
{
    od_sip_invite_dynamic_params *param = obj->params;

    param->invite_status = 0;
    param->bye_flg = 0;
    param->send_invite_flg = 0;
    obj->wait_pick_up_cnt = 0;
    memset(&obj->session_ctx, 0, sizeof(obj->session_ctx));
    obj->ctx->share->se_timer.refresh_cnt = 0;
    obj->cid = -1;
    obj->did = -1;
}

static void sip_invite_session_close(od_sip_invite_obj *obj)
{
    obj->real_time_rst = DEF_RST;
    obj->malloc_free_rst = DEF_RST;
}

static reg_node_no sip_invite_get_reg_idx(od_sip_invite_obj *obj)
{
    reg_node_no idx;

    /**
    * Prioritize the use of the registered node information, 
    * otherwise the registration node 1,
    */
    if (*obj->ctx->share->acc_info[REG_NODE_1].reg_status == REGISTER_OK) {
        idx = REG_NODE_1;
    } else if (*obj->ctx->share->acc_info[REG_NODE_2].reg_status == REGISTER_OK) {
        idx = REG_NODE_2;
    } else {
        idx = REG_NODE_1;
    }

    return idx;
}

static void sip_invite_add_header(od_sip_invite_obj *obj, osip_message_t *invite)
{
    char buf[64] = {0};
    od_media_config cfg;
    reg_node_no idx = sip_invite_get_reg_idx(obj);

    if (obj->ctx->cb.cfg_cb.get_cfg)
        obj->ctx->cb.cfg_cb.get_cfg(obj->ctx->cb.cfg_cb.h_callback, &cfg);
        
    osip_message_set_allow(invite, ALLOW_STRING); 
    osip_message_set_supported(invite, SUPPORT_STRING);
    snprintf(buf, sizeof(buf), "%d", obj->ctx->share->se_timer.sess_expires);
    osip_message_set_header(invite, Session_Expires, buf);
    snprintf(buf, sizeof(buf), "<sip:%s@%s:%d>", 
                obj->ctx->share->acc_info[idx].username,
                cfg.ipaddr,
                obj->ctx->engine_cfg.local_sip_port);
    osip_message_set_subject(invite, buf);
    osip_message_set_content_type(invite, CONTENT_TYPE_STRING);          
}

static int sip_invite_wait(od_sip_invite_obj *obj)
{
    od_sip_invite_dynamic_params *param = obj->params;

    if (param->send_invite_flg == 0)
        return OD_ERROR;

    if (get_phone_status(*obj->h_state_machine) != PHONE_STATUS_AVAILABLE) {
        param->send_invite_flg = 0;
        return OD_ERROR;
    }

    return OD_OK;
}

static int sip_invite_send_invite_to_remote(od_sip_invite_obj *obj)
{
    od_sip_invite_dynamic_params *param = obj->params;
    od_sip_share *share = obj->ctx->share;
    od_media_config cfg;
    osip_message_t *invite = NULL;
    char to[256] = {0};
    char from[256] = {0};
    char sdp[2048] = {0};
    reg_node_no idx = sip_invite_get_reg_idx(obj);

    OD_SIP_ASSERT_RETURN(share, OD_EFAULT);
    if (share->acc_info[idx].init_done != OD_EEXIST) {
        RPTERR("acc_info not ready!!");
        return OD_ERROR;
    }

    if (obj->ctx->cb.cfg_cb.get_cfg)
        obj->ctx->cb.cfg_cb.get_cfg(obj->ctx->cb.cfg_cb.h_callback, &cfg);
    
    snprintf(to, sizeof(to), "sip:%s@%s:%d", 
             param->dst_uri, 
             share->acc_info[idx].proxy_addr, 
             *share->acc_info[idx].proxy_port);
    
    snprintf(from, sizeof(from), "sip:%s@%s:%d", 
             share->acc_info[idx].username, 
             cfg.ipaddr, 
             obj->ctx->engine_cfg.local_sip_port);

    eXosip_lock(obj->ctx->ex_ctx);
    if (eXosip_call_build_initial_invite(obj->ctx->ex_ctx, &invite, to, from, NULL, NULL) != 0) {
        RPTERR("initial INVITE failed!!!");
        eXosip_unlock(obj->ctx->ex_ctx);
        return OD_ERROR;
    }

    /*add addtional Header!*/
    sip_invite_add_header(obj, invite);

    /*create inital SDP to remote, base on local #on_media_config*/
    if (od_media_create_initial_sdp(obj->ctx,
                                    sdp,
                                    sizeof(sdp),
                                    &obj->session_ctx.session_sdp_neg.local_sdp, 
                                    &cfg) == PJ_SUCCESS) {
        osip_message_set_body(invite, sdp, strlen(sdp));
    }else {
        RPTERR("create SDP error! will send INVITE with no SDP!!!");
    }

    obj->cid = eXosip_call_send_initial_invite(obj->ctx->ex_ctx, invite);
    eXosip_unlock(obj->ctx->ex_ctx);

    return (obj->cid > 0)? OD_OK : OD_ERROR;
}


static int sip_invte_call_answered(od_sip_invite_obj *obj)
{
    od_sip_invite_dynamic_params *param = obj->params;
    char *remote_sdp_char = NULL;
    osip_body_t *body = NULL;
    size_t str_length;
    
    OD_SIP_ASSERT_RETURN(obj->event, OD_ERROR);

    /*step 1: parse status code*/
    if (!obj->event->response) {
        RPTDBG("obj->event->response null error!");
        set_phone_status(*obj->h_state_machine, PHONE_STATUS_MEDIA_ERR);
        return OD_EBADFD;
    }
        
    if (obj->event->response->status_code != SIP_OK) {
        RPTDBG("status_code != 200 ok!");
        set_phone_status(*obj->h_state_machine, PHONE_STATUS_UNSUPPORT_MEDIA_TYPE);
        od_sip_send_ack(obj->ctx->ex_ctx, obj->event);
        return OD_EBADTYPE;
    }

    od_sip_timer_process_resp(obj->ctx, obj->event);
    
    /*step 2: get remote sdp in string*/
    if (osip_message_get_body(obj->event->response, 0, &body) < 0) {
        RPTERR("get remote INVITE body error!!");
        goto exit;
    }

    if (osip_body_to_str(body, &remote_sdp_char, &str_length) < 0) {
        RPTERR("body to str error!!");
        goto exit;
    }

    /*step 3: negotiate remote sdp with local offer*/
    obj->session_ctx.session_sdp_neg.session_type = OD_LOCAL_OFFER;
    if (od_media_sdp_negotiate(obj->ctx, 
                               remote_sdp_char, 
                               str_length, 
                               &obj->session_ctx) != PJ_SUCCESS) {
        RPTERR("sdp negotiate error!!");
        goto exit;
    }

    /*negotiate success, than send ACK*/
    od_sip_send_ack(obj->ctx->ex_ctx, obj->event);

    /*step 4: get negotiate result set to local encoder/decoder*/
    if (obj->ctx->cb.cfg_cb.set_cfg) {
        od_media_params *decode_param = &obj->session_ctx.decode_media;
        od_media_params *encode_param = &obj->session_ctx.encode_media;
        obj->ctx->cb.cfg_cb.set_cfg(obj->ctx->cb.cfg_cb.h_callback, decode_param);
        obj->ctx->cb.cfg_cb.set_cfg(obj->ctx->cb.cfg_cb.h_callback, encode_param);
    }

    /*step 5: set phone status*/
    set_phone_status(*obj->h_state_machine, PHONE_STATUS_CONVERSATION);
    param->invite_status = 1;
    obj->ctx->share->se_timer.last_refresh_time = od_sip_getCurTimeInMsec();

    /*step 6: restore did*/
    obj->did = obj->event->did;
    
    return OD_OK;

exit:
    set_phone_status(*obj->h_state_machine, PHONE_STATUS_LOCAL_ERR);
    od_sip_call_send_answer_with_code(obj->ctx->ex_ctx, obj->event, SIP_UNSUPPORTED_MEDIA_TYPE);
    return OD_ERROR;
}

static void sip_invite_call_closed(od_sip_invite_obj *obj)
{
    /*reset local encoder and decoder, close send or recv ctl*/
    if (obj->ctx->cb.codec_cb.reset_codec)
        obj->ctx->cb.codec_cb.reset_codec(obj->ctx->cb.codec_cb.h_codec);

    set_phone_status(*obj->h_state_machine, PHONE_STATUS_HANG);
    od_sip_call_send_answer_with_code(obj->ctx->ex_ctx, obj->event, SIP_OK);
    sip_invite_session_close(obj);
}


/**
* If there is an error in the session modification during the call, 
* the original call shall be maintained and the call state shall not be changed,
* in other word, do not call the #set_phone_status function.
*/
static int sip_invite_call_reinvite(od_sip_invite_obj *obj)
{
    char *remote_sdp_char = NULL;
    od_media_config capa_cfg;
    osip_body_t *body = NULL;
    size_t str_length;
    
    OD_SIP_ASSERT_RETURN(obj->event, OD_ERROR);

    /*step 1：get remote SDP to string*/
    if (!obj->event->request) {
        RPTERR("INVITE request NULL,error!");
        goto exit;
    }
    
    if (osip_message_get_body(obj->event->request, 0, &body) < 0) {
        RPTERR("get remote INVITE body error!!");
        goto exit;
    }

    if (osip_body_to_str(body, &remote_sdp_char, &str_length) < 0) {
        RPTERR("body to str error!!");
        goto exit;
    }

    /*step 2: negotiate*/
    if (obj->ctx->cb.cfg_cb.get_capa) //get local encoder/decoder capabilty
        obj->ctx->cb.cfg_cb.get_capa(obj->ctx->cb.cfg_cb.h_callback, &capa_cfg);

    obj->session_ctx.local_capa_config = &capa_cfg;
    obj->session_ctx.session_sdp_neg.session_type = OD_REMOTE_OFFER;
    if (od_media_sdp_negotiate(obj->ctx, 
                               remote_sdp_char, 
                               str_length, 
                               &obj->session_ctx) != PJ_SUCCESS) {
        RPTERR("sdp negotiate error!!");
        goto exit;
    }    

    /*setp 3: return 200OK with SDP*/
    {
        osip_message_t *answer = NULL;
        char result_sdp[1024] = {0};

        if (od_media_sdp_print(obj->session_ctx.session_sdp_neg.active_local_sdp,
                               result_sdp,
                               sizeof(result_sdp)) < 0) {
            RPTERR("result_sdp buf too short!!!");
            goto exit;
        }

        eXosip_lock(obj->ctx->ex_ctx);
        eXosip_call_build_answer(obj->ctx->ex_ctx, obj->event->tid, SIP_OK, &answer);
        sip_invite_add_header(obj, answer);
        osip_message_set_body(answer, result_sdp, strlen(result_sdp));
        eXosip_call_send_answer(obj->ctx->ex_ctx, obj->event->tid, SIP_OK, answer);
        eXosip_unlock(obj->ctx->ex_ctx);
    }

    /*step 4: restore did*/
    obj->did = obj->event->did;
                               
    /*step 5: get negotiate result set to local encoder/decoder*/
    if (obj->ctx->cb.cfg_cb.set_cfg) {
        od_media_params *decode_param = &obj->session_ctx.decode_media;
        od_media_params *encode_param = &obj->session_ctx.encode_media;
        obj->ctx->cb.cfg_cb.set_cfg(obj->ctx->cb.cfg_cb.h_callback, decode_param);
        obj->ctx->cb.cfg_cb.set_cfg(obj->ctx->cb.cfg_cb.h_callback, encode_param);
    }

    return OD_OK;

exit:
    set_phone_status(*obj->h_state_machine, PHONE_STATUS_LOCAL_ERR);
    od_sip_call_send_answer_with_code(obj->ctx->ex_ctx, obj->event, SIP_NOT_ACCEPTABLE_HERE);
    return OD_ERROR;
}


static int sip_invite_request_failure(od_sip_invite_obj *obj)
{
    if (!obj->event->response) {
        RPTWRN("response none!!!");
        goto exit;
    }

    RPTWRN("recv status_code[%d]", obj->event->response->status_code);
    switch(obj->event->response->status_code)
    {
        case SIP_BUSY_HERE:
            RPTWRN("remote busy!!");
            sip_invite_session_close(obj);
            goto exit;
            break;
            
        case SIP_UNAUTHORIZED:
        case SIP_PROXY_AUTHENTICATION_REQUIRED:
            eXosip_automatic_action(obj->ctx->ex_ctx);
            break;
        
        case SIP_SESSION_TIMER_TOO_SMALL:
            od_sip_timer_process_resp(obj->ctx, obj->event);
            eXosip_automatic_action(obj->ctx->ex_ctx);
            break;
            
        default:
            RPTWRN("automatic action to status_code:[%d]!!",
                    obj->event->response->status_code);
            eXosip_automatic_action(obj->ctx->ex_ctx);
            sip_invite_session_close(obj);
            goto exit;
            break;
    }

    return OD_OK;

exit:
    set_phone_status(*obj->h_state_machine, PHONE_STATUS_LOCAL_ERR);
    od_sip_send_ack(obj->ctx->ex_ctx, obj->event);
    return OD_ERROR;
}


static void sip_invite_call_release(od_sip_invite_obj *obj)
{
    /*reset local encoder/decoder*/
    if (obj->ctx->cb.codec_cb.reset_codec)
        obj->ctx->cb.codec_cb.reset_codec(obj->ctx->cb.codec_cb.h_codec);

    sip_invite_session_close(obj);
}

static void sip_invite_send_bye(od_sip_invite_obj *obj)
{
    eXosip_lock(obj->ctx->ex_ctx);
    eXosip_call_terminate(obj->ctx->ex_ctx, obj->cid, obj->did);
    eXosip_unlock(obj->ctx->ex_ctx);

    /*reset local encoder/decoder*/
    if (obj->ctx->cb.codec_cb.reset_codec)
        obj->ctx->cb.codec_cb.reset_codec(obj->ctx->cb.codec_cb.h_codec);

    set_phone_status(*obj->h_state_machine, PHONE_STATUS_HANG);
    sip_invite_session_close(obj);
}

static void sip_invite_monitor_conversation(od_sip_invite_obj *obj)
{
    od_sip_invite_dynamic_params *param = obj->params;

    if (param->invite_status == 0)
        return;

    if (param->bye_flg) {
        sip_invite_send_bye(obj);
    }

    if (obj->ctx->share->se_timer.refresher != RFH_UNSUPPORT) {
        uint32_t refresh_interval = (od_sip_getCurTimeInMsec() \
                                    - obj->ctx->share->se_timer.last_refresh_time)/1000; //unit:second
        if (refresh_interval > (obj->ctx->share->se_timer.sess_expires/2)) {
            if (obj->ctx->share->se_timer.refresher == RFH_UAC) {
                od_sip_timer_create_update_req(obj->ctx, obj->did);
            }

            obj->ctx->share->se_timer.last_refresh_time = od_sip_getCurTimeInMsec();
            if (++(obj->ctx->share->se_timer.refresh_cnt) 
                    >= obj->ctx->share->se_timer.max_fail_times) {
                sip_invite_send_bye(obj);
            }
        }
    }
}

static void sip_invite_monitor_wait_pick_up(od_sip_invite_obj *obj)
{
    od_sip_invite_dynamic_params *param = obj->params;

    if (param->bye_flg 
          || (param->invite_status == 0 && obj->wait_pick_up_cnt++ > NO_RESPONSE_TIME_OUT)) {
        RPTDBG("CANCEL send!!");
        eXosip_lock(obj->ctx->ex_ctx);
        eXosip_call_terminate(obj->ctx->ex_ctx, obj->cid, -1);
        eXosip_unlock(obj->ctx->ex_ctx);

        set_phone_status(*obj->h_state_machine, PHONE_STATUS_CANCEL);
        sip_invite_session_close(obj);
    }
}

/*Monitor the session process and respond to user control instructions*/
static void sip_invite_monitor(od_sip_invite_obj *obj)
{
    switch(get_phone_status(*obj->h_state_machine))
    {
        case PHONE_STATUS_CONVERSATION:
            sip_invite_monitor_conversation(obj);
            break;

        case PHONE_STATUS_REMOTE_RINGING:
        case PHONE_STATUS_DIAL:
        case PHONE_STATUS_PROCEEDING:
        default:
            sip_invite_monitor_wait_pick_up(obj);
            break;
    }
}


static int sip_invite_handle_event(od_sip_invite_obj *obj)
{
    int retval = OD_OK;
    /*FIXME:should consider all #eXosip_event_type about
    * EXOSIP_CALL_XXX status.
    */

    switch(obj->event->type)  
    {  
        case EXOSIP_CALL_INVITE:  
            RPTDBG("EXOSIP_CALL_INVITE");
            break;  

        case EXOSIP_CALL_PROCEEDING:
            RPTDBG("EXOSIP_CALL_PROCEEDING");
            break;

        case EXOSIP_CALL_RINGING:  
            RPTDBG("EXOSIP_CALL_RINGING");
            set_phone_status(*obj->h_state_machine, PHONE_STATUS_REMOTE_RINGING);
            break;  

        case EXOSIP_CALL_ANSWERED:
            RPTDBG("EXOSIP_CALL_ANSWERED");
            retval = sip_invte_call_answered(obj);
            break;  

        case EXOSIP_CALL_CLOSED:  
            RPTDBG("EXOSIP_CALL_CLOSED");
            sip_invite_call_closed(obj);
            break;  

        case EXOSIP_CALL_ACK:  
            RPTDBG("EXOSIP_CALL_ACK");
            break;

        case EXOSIP_CALL_REINVITE: 
            RPTDBG("EXOSIP_CALL_REINVITE");
            if (sip_invite_call_reinvite(obj) != OD_OK) {
                RPTERR("call_reinvite negotiate failed!!");
            }
            break;

        case EXOSIP_CALL_MESSAGE_NEW:
            RPTDBG("EXOSIP_CALL_MESSAGE_NEW");
            retval = od_sip_msg_call_message_new(obj->ctx, obj->event, obj->cid); 
            break; 

        case EXOSIP_CALL_MESSAGE_ANSWERED:
            RPTDBG("EXOSIP_CALL_MESSAGE_ANSWERED");
            retval = od_sip_msg_call_message_answerd(obj->ctx, obj->event);
            break;

        case EXOSIP_CALL_MESSAGE_REQUESTFAILURE:
            RPTDBG("EXOSIP_CALL_MESSAGE_REQUESTFAILURE");
            /*TODO*/
            break;

        case EXOSIP_CALL_REQUESTFAILURE:
            RPTDBG("EXOSIP_CALL_REQUESTFAILURE");
            retval = sip_invite_request_failure(obj);
            break;
            
        case EXOSIP_CALL_RELEASED:
            RPTDBG("EXOSIP_CALL_RELEASED");
            sip_invite_call_release(obj);
            break; 

        default:
            RPTWRN("un-process:[%s]\n", obj->event->textinfo);
            break;
    }

    eXosip_event_free(obj->event);
    return retval;
}


static int sip_invite_start_stop(od_sip_invite_obj *obj)
{
    static pj_thread_desc thread_desc;
    pj_thread_t *thread;
    pipeline_attrs fAttrs = pipeline_attrs_default;
    
    obj->ctx = od_sip_lib_start();
    OD_SIP_ASSERT_RETURN(obj->ctx, OD_ERROR);

    if (!pj_thread_is_registered()) {
        pj_bzero(thread_desc, sizeof(pj_thread_desc));
        pj_thread_register(obj->task_name, thread_desc, &thread);
    }

    /*create event pipe*/
    obj->g_event_pipe = pipeline_create(&fAttrs);
    if (!obj->g_event_pipe) {
        RPTERR("create event pipe error!!");
        return OD_ERROR;
    }

    /*register to event distributor pthread to get event!!*/
    obj->evt_que.tsk_id = obj->group_id;
    obj->evt_que.obj_id = &obj->cid;
    obj->evt_que.tsk_name = obj->task_name;
    obj->evt_que.queue_type = OD_SIP_EVT_QUEUE_TYPE_INVITE;
    obj->evt_que.g_event_pipe = obj->g_event_pipe;
    if (od_sip_evt_distrb_add(&obj->evt_que) != OD_OK) {
        RPTERR("add handle_event que pipe error!");
        return OD_ERROR;
    }

    /*wait until STATE-MACHINE start!!*/
    while(*obj->h_state_machine == NULL)
        od_msleep(100);

    sip_invite_reset_all_status_flg(obj);

    return OD_OK;
}

static void sip_invite_start_stop_delete(od_sip_invite_obj *obj)
{
    od_sip_lib_stop();
    od_sip_evt_distrb_remove(&obj->evt_que);
    pipeline_delete(obj->g_event_pipe);
}

static int sip_invite_malloc_free(od_sip_invite_obj *obj)
{
    if (sip_invite_wait(obj) != OD_OK) {
        od_msleep(500);

        /*test code*/
        if (0)
        {
            static int cnt_to_invite = 0;
            if (cnt_to_invite++ > 15
                 && get_phone_status(*obj->h_state_machine) == PHONE_STATUS_AVAILABLE) {
                cnt_to_invite = 0;
                goto START_INVITE;
            }
                
        }/*test code end*/
        
        return OD_ERROR;
    }

START_INVITE: //test code end
    set_phone_status(*obj->h_state_machine, PHONE_STATUS_DIAL);

    od_sip_clear_event_fifo(obj->g_event_pipe);

    if (sip_invite_send_invite_to_remote(obj) != OD_OK) {
        RPTERR("sip build INVITE and send error!!!");
        set_phone_status(*obj->h_state_machine, PHONE_STATUS_LOCAL_ERR);
        sip_invite_session_close(obj);
        return OD_ERROR;
    }
    
    return OD_OK;
}

static void sip_invite_malloc_free_delete(od_sip_invite_obj *obj)
{
    sip_invite_reset_all_status_flg(obj);
    pj_pool_reset(obj->ctx->pool); /*FIXME:need a lock*/
}

static int sip_invite_process(od_sip_invite_obj *obj)
{
    sip_invite_monitor(obj);
        
    if(od_sip_get_event(obj->g_event_pipe, &obj->event) < 0) {
        od_msleep(50);
        return OD_OK;
    }
        
    return sip_invite_handle_event(obj);
}

od_sip_invite_handle od_sip_invite_create(od_sip_invite_static_params *static_params,
                                            od_sip_invite_dynamic_params *dynamic_params)
{
    od_sip_invite_obj *obj = NULL;
    od_sip_invite_dynamic_params *params = NULL;

    OD_SIP_ASSERT_RETURN(static_params, NULL);
    OD_SIP_ASSERT_RETURN(static_params->h_state_machine, NULL);

    obj = od_task_create((char *)static_params->task_name, sizeof(od_sip_invite_obj));
    OD_SIP_ASSERT_RETURN(obj, NULL);

    obj->group_id = static_params->group_id;
    obj->h_state_machine = static_params->h_state_machine;
    obj->tab = od_sip_invite_dynamic_params_def;
    obj->params_nums = sizeof(od_sip_invite_dynamic_params_def) / sizeof(params_def) - 1;

    params = od_calloc(1, sizeof(od_sip_invite_dynamic_params));
    OD_SIP_ASSERT_RETURN(params, NULL);

    if (NULL == dynamic_params)
        memcpy(params, &glb_sip_invite_dynamic_params_default, sizeof(od_sip_invite_dynamic_params));
    else
        memcpy(params, dynamic_params, sizeof(od_sip_invite_dynamic_params));

    obj->params = (void *)params;
    obj->stack_size = SIP_INVITE_TSK_STACK_SIZE;
    obj->pf_start_stop  = (void *)sip_invite_start_stop;
    obj->pf_malloc_free = (void *)sip_invite_malloc_free;
    obj->pf_rt_set = NULL;
    obj->pf_process     = (void *)sip_invite_process;
    obj->pf_malloc_free_delete = (void *)sip_invite_malloc_free_delete;
    obj->pf_start_stop_delete = (void *)sip_invite_start_stop_delete;
    
    RPTDBG("create success");
    return (od_sip_invite_handle)obj;
}


/*_*/

